package step

import (
	"crypto/ecdsa"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpccrypto "github.com/wanchain/go-mpc/storeman/storemanmpc/crypto"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
)

type mpcPointGeneratorBtc struct {
	BaseGenerator
	seed [2]big.Int
	//message     map[uint64][2]big.Int
	message     map[discover.NodeID][2]big.Int
	result      [2]big.Int
	preValueKey string
	smcer       mpcprotocol.SchnorrMPCer
	ContextId   uint64
	GrpId       string
}

func createPointGeneratorBtc(preValueKey string) *mpcPointGeneratorBtc {
	return &mpcPointGeneratorBtc{message: make(map[discover.NodeID][2]big.Int), preValueKey: preValueKey}
}

func (point *mpcPointGeneratorBtc) initialize(totalNum int, result mpcprotocol.MpcResultInterface) error {
	log.SyslogDebug("mpcPointGenerator.initialize begin")

	_, point.GrpId, _ = osmconf.GetGrpId(result)
	value, err := result.GetValue(point.preValueKey)
	if err != nil {
		log.SyslogErr("mpcPointGenerator.initialize get preValueKey fail")
		return err
	}

	curve := crypto.S256()
	x, y := curve.ScalarBaseMult(value[0].Bytes())
	if x == nil || y == nil {
		log.SyslogErr("mpcPointGenerator.ScalarBaseMult fail. err:%s", mpcprotocol.ErrPointZero.Error())
		return mpcprotocol.ErrPointZero
	}

	point.seed = [2]big.Int{*x, *y}

	log.SyslogDebug("mpcPointGenerator.initialize succeed")
	return nil
}

func (point *mpcPointGeneratorBtc) calculateResult() error {
	log.SyslogDebug("mpcPointGenerator.calculateResult begin")
	result := new(ecdsa.PublicKey)
	result.Curve = crypto.S256()
	var nodeId []*discover.NodeID
	var i = 0
	var indexes []big.Int
	for id, value := range point.message {
		if i == 0 {
			result.X = new(big.Int).Set(&value[0])
			result.Y = new(big.Int).Set(&value[1])
			i++
		} else {
			result.X, result.Y = crypto.S256().Add(result.X, result.Y, &value[0], &value[1])
		}
		nodeIdBytes := id
		nodeId = append(nodeId, &nodeIdBytes)
		index, _ := osmconf.GetOsmConf().GetInxByNodeId(point.GrpId, &id)
		indexBig := big.NewInt(0).SetUint64(uint64(index))
		indexes = append(indexes, *indexBig)
	}

	bigCollection, _ := osmconf.BuildDataByIndexes(&indexes)
	log.SyslogInfo("btc_mpc_point_generator", "len(peer)", len(nodeId),
		"calculateResult peers", discover.ArrNodeIdpsToStr(nodeId),
		"bigcollection", hexutil.Encode(bigCollection.Bytes()),
		"ctx", point.GetContextId())

	if !mpccrypto.ValidatePublicKey(result) {
		log.SyslogErr("mpcPointGenerator.ValidatePublicKey fail. err:%s", mpcprotocol.ErrPointZero.Error())
		return mpcprotocol.ErrPointZero
	}

	point.result = [2]big.Int{*result.X, *result.Y}

	log.SyslogDebug("mpcPointGenerator.calculateResult succeed")
	return nil
}

func (point *mpcPointGeneratorBtc) SetSchnorrMpcer(smcer mpcprotocol.SchnorrMPCer) error {
	point.smcer = smcer
	return nil
}

func (point *mpcPointGeneratorBtc) SetContextId(contextId uint64) {
	point.ContextId = contextId
}

func (point *mpcPointGeneratorBtc) GetContextId() uint64 {
	return point.ContextId
}

func (msg *mpcPointGeneratorBtc) SetMpcContext(mpc MpcContextFunc) {
	msg.mpc = mpc
}

func (msg *mpcPointGeneratorBtc) GetMpcContext() MpcContextFunc {
	return msg.mpc
}
